package com.cicc.wm.lock.util;

import com.cicc.wm.lock.DistributedLock;
import com.cicc.wm.lock.LockRegistryFactory;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * 锁工具类
 */
public final class LockUtil {
    private LockUtil() {
    }

    /**
     * 尝试加锁
     *
     * @param <R>             返回值类型
     * @param lockKey         锁key
     * @param lockSuccessCall 锁成功回调
     * @param lockFailCall    锁失败回调
     * @return 执行返回值
     * @throws Exception 异常信息
     */
    public static <R> R tryLock(String lockKey, Callable<R> lockSuccessCall, Callable<R> lockFailCall) throws Exception {
        return tryLock(lockKey, 0, TimeUnit.SECONDS, lockSuccessCall, lockFailCall);
    }

    /**
     * 尝试加锁
     *
     * @param <R>             返回值类型
     * @param lockKey         锁key
     * @param waiteTime       获取锁等待时间
     * @param timeUnit        时间单位
     * @param lockSuccessCall 锁成功回调
     * @param lockFailCall    锁失败回调
     * @return 执行返回值
     * @throws Exception 异常信息
     */
    public static <R> R tryLock(String lockKey, int waiteTime, TimeUnit timeUnit,
                                Callable<R> lockSuccessCall, Callable<R> lockFailCall) throws Exception {
        return tryLock(lockKey, waiteTime, -1, timeUnit, lockSuccessCall, lockFailCall);
    }

    /**
     * 尝试加锁
     *
     * @param <R>             返回值类型
     * @param lockKey         锁key
     * @param waiteTime       获取锁等待时间
     * @param leaseTime       锁持有时间
     * @param timeUnit        时间单位
     * @param lockSuccessCall 锁成功回调
     * @param lockFailCall    锁失败回调
     * @return 执行返回值
     * @throws Exception 异常信息
     */
    public static <R> R tryLock(String lockKey, int waiteTime, int leaseTime, TimeUnit timeUnit,
                                Callable<R> lockSuccessCall, Callable<R> lockFailCall) throws Exception {
        DistributedLock distributedLock = LockRegistryFactory.getLockRegistry().obtain(lockKey);
        if (distributedLock.tryLock(waiteTime, leaseTime, timeUnit)) {
            try {
                return lockSuccessCall.call();
            } finally {
                distributedLock.unlock();
            }
        } else {
            return lockFailCall.call();
        }
    }
}
